
namespace apeng {
    /**单个池子 */
    export class PoolOnce<V> {
        private data: V[] = []

        constructor(
            public maxCount: number,
            public onObj: () => V,
            public onClear: (data: V) => void,
            public onDestroy?: (data: V) => void
        ) { }

        /**实例化个数 */
        public instantiate(size: number) {
            for (let i = 0; i < size; i++) {
                let obj = this.onObj()!
                this.data[this.data.length] = obj
            }
        }

        public get<T extends V>(): T {
            let data = this.data.pop()!
            if (!data)
                data = this.onObj()!
            this.onClear(data)
            return data as V as T
        }

        public put(data: V) {
            this.onClear(data)
            let length = this.data.length
            if (length > this.maxCount) {
                let destroyValue = this.data.shift()
                if (destroyValue) {
                    this.onClear(destroyValue)
                    if (this.onDestroy)
                        this.onDestroy(destroyValue)
                }
                this.put(data)
            }
            else
                this.data[length] = data
        }

        public clear() {
            for (let _data of this.data) {
                this.onClear(_data)
                if (this.onDestroy)
                    this.onDestroy(_data)
            }
            this.data.length = 0
        }

        public size() { return this.data.length }

    }

    /**多池子 */
    export class Pool<K, V> {

        private datas: Map<K, PoolOnce<V>> = new Map()

        constructor(
            private maxCount: number,
            private onObj: (url: K) => V,
            private onClear: (data: V, url: K) => void,
            private onDestroy?: (data: V, url: K) => void
        ) { }


        /**实例化个数 */
        public instantiate(url: K, size: number) { return this.pool(url!).instantiate(size) }

        public get<T extends V>(url: K): T { return this.pool(url!).get() }
        public put<T extends V>(data: T) { return this.pool((data as any).poolLoadUrl).put(data) }
        public clear() {
            this.datas.forEach((pool, k) => pool.clear())
            this.datas.clear()
        }

        public size(): number {
            let count = 0
            this.datas.forEach(pool => {
                count += pool.size()
            })
            return count
        }

        public pool(url: K) {
            let data = this.datas.get(url)
            if (!data) {
                data = new PoolOnce(this.maxCount, () => {
                    let obj = this.onObj(url) as any
                    obj.poolLoadUrl = url
                    return obj
                }, (_data) => this.onClear(_data, url)
                    , this.onDestroy ? (_data) => this.onDestroy!(_data, url) : null!)
                // 默认池子
                this.datas.set(url || "pool" as any, data)
            }
            return data
        }

    }

    /**池子数组 */
    export class PoolArray<V>  {

        public data: V[] = []

        constructor(
            private pool: PoolOnce<V>
        ) { }

        public clear() {
            for (let _data of this.data)
                this.pool.put(_data)
            this.data.length = 0
        }

        public add() {
            let data = this.pool.get()
            this.data[this.data.length] = data
            return data
        }

        public size() {
            return this.data.length
        }

        public get(index: number) { return this.data[index] }

    }

    export const CPoolArray = new PoolOnce<any[]>(100000, () => [], data => data.length = 0)

}
